// TcpServer.hpp
#pragma once
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <cstdlib>
#include <cstring>
#include "Log.hpp"

#define BACKLOG 5

enum
{
    SOCKET_ERR = 1,
    BIND_ERR,
    LISTEN_ERR,
    ARGC_ERR
};

class TcpServer
{
private:
    TcpServer(int port)
        : _port(port), _listensock(-1)
    {
    }
    TcpServer(const TcpServer &s) {}

public:
    ~TcpServer()
    {
        if (_listensock >= 0)
            close(_listensock);
    }
    static TcpServer *GetInstance(int port)
    {
        if (svr == nullptr)
        {
            static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
            pthread_mutex_lock(&mutex);
            if (svr == nullptr)
            {
                svr = new TcpServer(port);
                svr->InitServer();
            }
            pthread_mutex_unlock(&mutex);
        }
        return svr;
    }
    void InitServer()
    {
        Socket();
        Bind();
        Listen();
        LOG(INFO, "tcp_server init success");
    }
    
    int Sock()
    {
        return _listensock;
    }
private:
    void Socket()
    {
        _listensock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listensock < 0)
        {
            LOG(FATAL, "socket error");
            exit(SOCKET_ERR);
        }
        LOG(INFO, "create socket success");

        // 地址端口复用
        int opt = 1;
        setsockopt(_listensock, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
    }
    void Bind()
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = INADDR_ANY; // 云服务器不能直接绑定公网IP

        if (bind(_listensock, (struct sockaddr *)&local, sizeof(local)))
        {
            LOG(FATAL, "bind error");
            exit(BIND_ERR);
        }
        LOG(INFO, "bind socket success");
    }
    void Listen()
    {
        if (listen(_listensock, BACKLOG))
        {
            LOG(FATAL, "listen socket error");
            exit(LISTEN_ERR);
        }
        LOG(INFO, "listen socket success");
    }

private:
    int _listensock;
    uint16_t _port;

    static TcpServer *svr;
};

TcpServer *TcpServer::svr = nullptr;